package utils

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"io"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

type HttpClient struct {
	Client    *http.Client
	req       *http.Request
	Url       string
	params    map[string]string
	formData  map[string]string
	HeaderMap map[string]string
	dataJson  string
	method    string
}

func NewHttpClient() *HttpClient {
	return &HttpClient{
		Client: &http.Client{
			Timeout: 60 * time.Second, // 超时时间60秒
		},
		params:   make(map[string]string),
		formData: make(map[string]string),
		HeaderMap: map[string]string{
			"Content-Type": "application/json", // 默认json请求
		},
	}
}

// 添加客户端配置
func (httpClient *HttpClient) AddCConfig(c *http.Client) *HttpClient {
	httpClient.Client = c
	return httpClient
}

// 设置请求超时时间
func (httpClient *HttpClient) SetTimeout(timeout time.Duration) *HttpClient {
	httpClient.Client.Timeout = timeout
	return httpClient
}

// "http://proxy-server-address:port"   用户密码可以为空
func (httpClient *HttpClient) AddProxy(proxyUrl, username, password string) *HttpClient {
	proxyUrl1, err := url.Parse(proxyUrl)
	if err != nil {
		panic(err)
	}
	if username != "" && password != "" {
		proxyUrl1.User = url.UserPassword(username, password)
	}
	httpClient.Client.Transport = &http.Transport{
		Proxy: http.ProxyURL(proxyUrl1),
	}
	return httpClient
}

// 请添加cookie
func (httpClient *HttpClient) AddCookie(cookie *http.Cookie) *HttpClient {
	httpClient.Client.Jar.SetCookies(httpClient.req.URL, []*http.Cookie{cookie})
	return httpClient
}

// 客户端跳过服务端的https证书验证
func (httpClient *HttpClient) SkipVerify() *HttpClient {
	httpClient.Client.Transport = &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	return httpClient
}

// 双向认证及客户端会验证服务端的证书   certFile:客户端证书client.pem  keyFile:客户端私钥 client.key
func (httpClient *HttpClient) AddCert(certFile, keyFile string) *HttpClient {
	clientCertPool := x509.NewCertPool()
	certBytes, err := os.ReadFile(certFile)
	if err != nil {
		panic("Unable to read cert.pem")
	}
	// 解析证书
	ok := clientCertPool.AppendCertsFromPEM(certBytes)
	if !ok {
		panic("failed to parse root certificate")
	}
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		panic(err)
	}
	httpClient.Client.Transport = &http.Transport{
		TLSClientConfig: &tls.Config{
			RootCAs:      clientCertPool,
			Certificates: []tls.Certificate{cert},
		},
	}
	return httpClient
}

// 添加请求头
func (httpClient *HttpClient) AddHeader(key, value string) *HttpClient {
	httpClient.HeaderMap[key] = value
	return httpClient
}

// 添加请求参数
func (httpClient *HttpClient) AddParams(key, value string) *HttpClient {
	httpClient.params[key] = value
	return httpClient
}

// 添加请求参数
func (httpClient *HttpClient) AddFormData(key, value string) *HttpClient {
	httpClient.AddHeader("Content-Type", "application/x-www-form-urlencoded")
	httpClient.formData[key] = value
	return httpClient
}

// 添加body数据
func (httpClient *HttpClient) AddData(data string) *HttpClient {
	httpClient.AddHeader("Content-Type", "application/json")
	httpClient.dataJson = data
	return httpClient
}

// 添加请求url
func (httpClient *HttpClient) AddUrl(url string) *HttpClient {
	httpClient.Url = url
	return httpClient
}

// get请求
func (httpClient *HttpClient) Get() *HttpClient {
	httpClient.method = "GET"
	return httpClient
}

// post请求
func (httpClient *HttpClient) Post() *HttpClient {
	httpClient.method = "POST"
	return httpClient
}

// put请求
func (httpClient *HttpClient) Put() *HttpClient {
	httpClient.method = "PUT"
	return httpClient
}

// delete请求
func (httpClient *HttpClient) Delete() *HttpClient {
	httpClient.method = "DELETE"
	return httpClient
}

// 同步
func (httpClient *HttpClient) Sync() (string, error) {
	return httpClient.do()
}

// 异步
func (httpClient *HttpClient) Async(fun func(str string, err error)) {
	go func() {
		do, err := httpClient.do()
		fun(do, err)
	}()
}

// 发起请求
func (httpClient *HttpClient) do() (string, error) {
	err := httpClient.configRequestContent()
	if err != nil {
		return "", err
	} //设置请求内容
	response, err := httpClient.Client.Do(httpClient.req)
	if err != nil {
		return "", err
	}
	defer response.Body.Close()
	b, err := io.ReadAll(response.Body)
	return string(b), err
}

func (httpClient *HttpClient) configRequestContent() error {
	//init url params
	requestUrl, err := initParams(httpClient.Url, httpClient.params)
	if err != nil {
		return err
	}
	//init header
	v, ok := httpClient.HeaderMap["Content-Type"]
	if ok && v == "application/json" {
		if err != nil {
			return err
		}
		httpClient.req, err = initHeader(requestUrl, httpClient.HeaderMap, httpClient.method, strings.NewReader(httpClient.dataJson))
		if err != nil {
			return err
		}
		return nil
	}

	if ok && v == "application/x-www-form-urlencoded" {
		data := convertDataToJsonForForm(httpClient.formData)
		httpClient.req, err = initHeader(requestUrl, httpClient.HeaderMap, httpClient.method, strings.NewReader(data.Encode()))
		if err != nil {
			return err
		}
		return nil
	}

	if ok && v == "multipart/form-data" {
		//TODO
		return nil
	}
	return errors.New("Content-Type is not support")
}

// 将map转换为url.Values 用于application/x-www-form-urlencoded
func convertDataToJsonForForm(maps map[string]string) *url.Values {
	values := url.Values{}
	if maps != nil {
		for key, value := range maps {
			values.Set(key, value)
		}
	}

	return &values
}

func initParams(urlPath string, params map[string]string) (*url.URL, error) {
	if params == nil {
		return url.ParseRequestURI(urlPath)
	}
	urlParams := url.Values{}
	for key, value := range params {
		urlParams.Set(key, value)
	}
	requestURI, err := url.ParseRequestURI(urlPath)
	if err != nil {
		return nil, err
	}
	requestURI.RawQuery = urlParams.Encode()

	return requestURI, nil
}

func initHeader(requestUrl *url.URL, headers map[string]string, method string, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, requestUrl.String(), body)
	if err != nil {
		return nil, err
	}

	//set header
	for key, value := range headers {
		req.Header.Add(key, value)
	}

	return req, nil
}

// 上传文件
func (httpClient *HttpClient) UploadFile(fileKey, filePath string) {
	UploadFile(httpClient.Url, fileKey, filePath)
}

// 批量上传文件
func (httpClient *HttpClient) UploadFiles(fileKey string, filePaths []string) {
	UploadFiles(httpClient.Url, fileKey, filePaths)
}

// 下载文件
func (httpClient *HttpClient) DownloadFile(filePath string) {
	DownloadFile(httpClient.Url, filePath)
}
